﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Net;
using System.Threading.Tasks;

namespace TriExporterNET.HTTPServer
{
    public class Server
    {
        List<Stuff.StuffFileEntry> List;
        HttpListener server = new HttpListener();
        public static Server Setup(List<Stuff.StuffFileEntry> List)
        {
            var ret = new Server();
            ret.server.Prefixes.Add(Adress);
            ret.List = List;
            ret.server.Start();
            //Task.Factory.StartNew(() =>
            //{
                AsyncCallback ac = null;
                ac = new AsyncCallback(iar =>
                {
                    var obj = iar.AsyncState as Server;
                    var context = obj.server.EndGetContext(iar);
                    Task.Factory.StartNew(() => obj.DoContext(context));
                    obj.server.BeginGetContext(ac, obj);
                });
                ret.server.BeginGetContext(ac, ret);
            //});
            return ret;
        }

        public void Stop()
        {
            server.Stop();
        }

        public const int port = 5555;
        static string adress;
        public static string Adress
        {
            get
            {
                if (string.IsNullOrEmpty(adress))
                    adress = string.Format("http://localhost:{0}/", port);
                return adress;
            }
        }

        void DoContext(HttpListenerContext context)
        {
            var req = context.Request;
            var response = context.Response;
            var uri = req.Url;// new Uri(Adress + request.URI);
            var file = uri.LocalPath.ToLower().Replace('/', '\\').TrimStart('\\').Replace('+', ' ');
            if (file == "favicon.ico")
            {
                response.AddHeader("Cache-Control", "max-age=3600");
                response.ContentType = "image/vnd.microsoft.icon";
                using (var stream = response.OutputStream)
                {
                    Properties.Resources.Game.Save(stream);
                }
                return;
            }

            if (file.StartsWith("img\\bi"))
            {
                var key = Path.GetFileNameWithoutExtension(file);
                Image img = null;
                switch (key)
                {

                    case "mp3":
                    case "ogg":
                    case "m3u":
                    case "wav":
                        img = (Image)Properties.Resources.ResourceManager.GetObject("music");
                        break;
                    case "css":
                    case "fx":
                    case "fxh":
                        img = (Image)Properties.Resources.ResourceManager.GetObject("txt");
                        break;
                    default:
                        img = (Image)Properties.Resources.ResourceManager.GetObject(key);
                        if (img == null)
                            img = (Image)Properties.Resources.ResourceManager.GetObject("unk");
                        break;
                }
                if (img != null)
                {
                    response.ContentType = "image/png";
                    response.AddHeader("Cache-Control", "max-age=300");
                    using (var stream = response.OutputStream)
                    {
                        img.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
                    }
                }
                else
                    SendErorr(response);
                return;
            }
            response.AddHeader("Cache-Control", "no-cache");
            string stufffile = "";
            if (!string.IsNullOrEmpty(uri.Query))
            {
                var par = uri.Query.TrimStart('?').Split('&');
                for (int i = 0; i < par.Length; i++)
                {
                    var par2 = par[i].Split('=');
                    if (par2[0] == "sf")
                    {
                        stufffile = par2[1];
                    }
                }
            }
            var sf = List.FirstOrDefault(x => x.FullName == file && x.StuffFullName.EndsWith(stufffile));
            if (sf != null)
            {
                using (var src = sf.GetStream())
                {
                    response.ContentType = getMimeFromFile(src);
                    response.ContentLength64 = sf.Length;
                    response.KeepAlive = false;
                    response.SendChunked = false;
                    src.Seek(0, SeekOrigin.Begin);
                    try
                    {
                        using (var dest = response.OutputStream)
                        {

                            src.CopyTo(dest, (int)src.Length);
                        }
                    }
                    catch { }
                }
            }
            else
            {
                if (file.EndsWith("\\"))
                    file = file.TrimEnd('\\');
                var dirs = List.Where(x => x.Path.StartsWith(file)).SelectMany(x => x.Path.SelectAllPaths()).Distinct(EC.Instance).Where(x => x.Item3 == file)
                    .Select(ret => new Tuple<string, string>(ret.Item1, ret.Item2));
                StringBuilder sb = new StringBuilder();
                dirs.ToList().ForEach(dir =>
                {
                    sb.Append("<tr><td colspan='2'><a href='");
                    //sb.Append(Adress);
                    sb.Append('/');
                    sb.Append(dir.Item1);
                    sb.Append("'><img src='/img/bi/dir.png'/>&nbsp;");
                    sb.Append(dir.Item2);
                    sb.Append("</a></td><td colspan='2'>dir</td><tr>");
                });
                List.Where(x => x.Path == file).ToList().ForEach(f =>
                {
                    sb.Append("<tr><td>&nbsp;&nbsp;</td><td><a href='");
                    //sb.Append(Adress);
                    sb.Append('/');
                    sb.Append(f.FullName);
                    sb.Append("?sf=");
                    sb.Append(f.StuffName);
                    sb.Append("'><img src='/img/bi/");
                    sb.Append(f.ExtensionDotless);
                    sb.Append(".png'/>&nbsp;");
                    sb.Append(f.Name);
                    sb.Append("</a></td><td align='right'>");
                    sb.Append(string.Format("{0:0.00}", f.Length / 1024.0));
                    sb.Append("<td>&nbsp;KB</td></td></tr>");
                });

                if (sb.Length > 0)
                {
                    if (!string.IsNullOrEmpty(file))
                    {
                        sb.Insert(0, "/..'><img src='/img/bi/dir.png'/>&nbsp;..</a></td><td colspan='2'>dir</td></tr>");
                        sb.Insert(0, file);
                        sb.Insert(0, '/');
                        //sb.Insert(0, Adress);
                        sb.Insert(0, "<tr><td colspan='2'><a href='");
                        sb.Insert(0, "'><img src='/img/bi/dir.png' />&nbsp;.</a></td><td colspan='2'>dir</td></tr>");
                        //sb.Insert(0, Adress);
                        sb.Insert(0, "<tr><td colspan='2'><a href='/");
                    }
                    sb.Insert(0, @"<html><head><title>Tri Exporter .NET</title> <style type='text/css'>
body { font-family: Verdana, Arial, sans-serif; margin: 40px; text-align: center; }
body, th, td { background-color: #ffffff; }
a:link { color: #666666; text-decoration: none; }
a:visited { color: #444444; text-decoration: none; }
a:hover { color: #666666; text-decoration: none; }
a:active { color: #660000; text-decoration: none; }
img { width: 16; height: 16; border 0px; }
h3 { margin: 0px auto; padding 0px 0px 10px 0px; }
table { background-color: #eeeeee; border: #cccccc solid 1px; width: 480px; margin: 0px auto; }
th { background-color: #4466aa; color: #ffffff; font-size: 11pt; font-weight: bold; text-align: left; padding: 2px; }
td { background-color: #eeeeee; color: #666666; font-size: 9pt; font-weight: normal; padding: 2px; }
</style></head><body><h3>Content of stuff files</h3><table cellspacing='0'><tr><th colspan='4'>content of \" + file + "</th></tr>");
                    sb.Append("</table></body></html>");
                    byte[] bytes = Encoding.UTF8.GetBytes(sb.ToString());
                    response.ContentType = "text/html";
                    response.ContentLength64 = bytes.LongLength;
                    response.OutputStream.Write(bytes, 0, bytes.Length);
                }
                else
                {
                    SendErorr(response);
                }
            }
        }

        [DllImport(@"urlmon.dll", CharSet = CharSet.Auto)]
        private extern static System.UInt32 FindMimeFromData(System.UInt32 pBC, [MarshalAs(UnmanagedType.LPStr)] System.String pwzUrl, [MarshalAs(UnmanagedType.LPArray)] byte[] pBuffer, System.UInt32 cbSize,
       [MarshalAs(UnmanagedType.LPStr)] System.String pwzMimeProposed, System.UInt32 dwMimeFlags, out System.UInt32 ppwzMimeOut, System.UInt32 dwReserverd);

        public static string getMimeFromFile(Stream fs)
        {
            byte[] buffer = new byte[256];
            {
                if (fs.Length >= 256)
                    fs.Read(buffer, 0, 256);
                else
                    fs.Read(buffer, 0, (int)fs.Length);
                try
                {
                    System.UInt32 mimetype;
                    FindMimeFromData(0, null, buffer, 256, null, 0, out mimetype, 0);
                    System.IntPtr mimeTypePtr = new IntPtr(mimetype);
                    string mime = Marshal.PtrToStringUni(mimeTypePtr);
                    Marshal.FreeCoTaskMem(mimeTypePtr);
                    return mime;
                }
                catch (Exception e)
                {
                    return "application/octet-stream";
                }
            }
        }

        void SendErorr(HttpListenerResponse response, string msg = "HTTP 500: Internal Server Error")
        {
            var buffer = Encoding.UTF8.GetBytes(string.Format("<html><body><h1>{0}</h1></body></html>", msg));
            response.ContentLength64 = buffer.LongLength;
            response.StatusCode = 500;
            response.ContentType = "text/html";
            using(var stream = response.OutputStream)
                stream.Write(buffer,0, buffer.Length);
        }
    }

    class EC:IEqualityComparer<Tuple<string, string, string>>
    {
        static EC me = new EC();
        public static EC Instance
        {
            get { return me; }
        }

        public bool Equals(Tuple<string, string, string> x, Tuple<string, string, string> y)
        {
            if (x == null || y == null)
                return false;
            return x.Item1 == y.Item1;
        }

        public int GetHashCode(Tuple<string, string,string> obj)
        {
            return obj.Item1.GetHashCode();
        }
    }
}
